﻿#region namespaces

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Xml;
using System.Xml.Linq;

#endregion

namespace ZOIL.DataBackend.BaseX
{
    /// <summary>
    ///     Implementation of <see cref="ISerializer" /> via <see cref="System.Xml.Serialization.XmlSerializer" />
    /// </summary>
    public sealed class XmlSerializer : ISerializer
    {
        public bool EscapeXQuerySpecialChars = true;
        private readonly ConcurrentDictionary<Type, System.Xml.Serialization.XmlSerializer> _xmlSerializersByType = new ConcurrentDictionary<Type, System.Xml.Serialization.XmlSerializer>();

        private System.Xml.Serialization.XmlSerializer GetSerialzerFor(Type t)
        {
            System.Xml.Serialization.XmlSerializer ser;
            if (!_xmlSerializersByType.TryGetValue(t, out ser))
            {
                ser = new System.Xml.Serialization.XmlSerializer(t);
                _xmlSerializersByType.TryAdd(t, ser);
            }
            return ser;
        }

        #region Implementation of ISerializer

        public XElement Serialize(object obj)
        {
            using (var memoryStream = new MemoryStream())
            {
                using (var writer = XmlWriter.Create(memoryStream, new XmlWriterSettings {OmitXmlDeclaration = true,}))
                {
                    var serializer = GetSerialzerFor(obj.GetType());
                    serializer.Serialize(writer, obj);
                }
                memoryStream.Position = 0;
                var xml = XElement.Load(memoryStream);
                if (EscapeXQuerySpecialChars)
                {
                    xml = xml.EscapeXQuerySpecialChars();
                }
                return xml;
            }
        }

        public object Deserialize(XElement xml, Type objectType)
        {
            using (var reader = xml.CreateReader())
            {
                var serializer = GetSerialzerFor(objectType);
                return serializer.Deserialize(reader);
            }
        }

        #endregion
    }
}